﻿using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using FirstFloor.Documents.Presentation.Controls;

namespace FirstFloor.Documents.Presentation.Behaviors
{
    /// <summary>
    /// Adds mouse support to the ScaleDocumentViewer control.
    /// </summary>
    public class ScaleDocumentViewerMouseBehavior
        : Behavior<ScaleDocumentViewer>
    {
        /// <summary>
        /// Identifies the LogicalMousePosition dependency property
        /// </summary>
        public static readonly DependencyProperty LogicalMousePositionProperty = DependencyProperty.Register("LogicalMousePosition", typeof(Point), typeof(ScaleDocumentViewerMouseBehavior), null);

        private Point dragStartPos;
        private Point dragStartViewportOrigin;
        private bool dragging;

        /// <summary>
        /// Called after the behavior is attached to an AssociatedObject.
        /// </summary>
        /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
        protected override void OnAttached()
        {
            this.AssociatedObject.MouseLeftButtonDown += OnMouseLeftButtonDown;
            this.AssociatedObject.MouseLeftButtonUp += OnMouseLeftButtonUp;
            this.AssociatedObject.LostMouseCapture += OnLostMouseCapture;
            this.AssociatedObject.MouseMove += OnMouseMove;
            this.AssociatedObject.MouseWheel += OnMouseWheel;
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.dragging = true;
            this.dragStartPos = e.GetPosition(this.AssociatedObject);
            this.dragStartViewportOrigin = this.AssociatedObject.ViewportOrigin;
            this.AssociatedObject.CaptureMouse();
        }

        private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            StopDragging();
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            var position = e.GetPosition(this.AssociatedObject);
            this.LogicalMousePosition = this.AssociatedObject.ElementToLogicalPoint(position);

            if (this.dragging) {
                var deltaX = this.dragStartPos.X - position.X;
                var deltaY = this.dragStartPos.Y - position.Y;

                var origin = this.dragStartViewportOrigin;
                origin.X += deltaX / this.AssociatedObject.ActualWidth * this.AssociatedObject.ViewportWidth;
                origin.Y += deltaY / this.AssociatedObject.ActualWidth * this.AssociatedObject.ViewportWidth;

                this.AssociatedObject.ViewportOrigin = origin;
            }
        }

        private void OnLostMouseCapture(object sender, MouseEventArgs e)
        {
            StopDragging();
        }

        private void OnMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var zoom = e.Delta > 0 ? 1.3 : 1 / 1.3;
            this.AssociatedObject.ZoomAboutLogicalPoint(zoom, this.LogicalMousePosition.X, this.LogicalMousePosition.Y);
        }

        private void StopDragging()
        {
            this.AssociatedObject.ReleaseMouseCapture();
            this.dragging = false;
        }

        /// <summary>
        /// Gets the logical mouse position.
        /// </summary>
        /// <value>The logical mouse position.</value>
        public Point LogicalMousePosition
        {
            get { return (Point)GetValue(LogicalMousePositionProperty); }
            private set { SetValue(LogicalMousePositionProperty, value); }
        }
    }
}
